﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;


namespace THFDownloader
{
    public partial class frmDownloader : Form
    {
        Stopwatch sw = new Stopwatch();    // The stopwatch which we will be using to calculate the download speed
        private Queue<string> _files = new Queue<string>();

        public frmDownloader()
        {
            InitializeComponent();
            //Prevent Flickering, This Produces Smooth Progression
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true); 
            
            // EDIT pbBackground.Tag and replace with URL.
            // http://s3.amazonaws.com/THFMAIN/
            try
            {
                pbBackground.ImageLocation = pbBackground.Tag + "background.jpg";
                pbHeader.ImageLocation = pbBackground.Tag + "header.jpg";
            }
            catch
            {
                MessageBox.Show("Please check your internet connection.");
            }
            pbBackground.Refresh();
            pbHeader.Refresh();
        }

// INITIAL DOWNLOADER LOGIC
        private void Downloader_Start(object sender, EventArgs e)
        {

            if (!File.Exists("THFDownloader.cfg")) //Check for the configuration file, if not found then push the settings box
            {
                //If no .cfg, then force player to set location of eqgame.exe
                if (!File.Exists("eqgame.exe"))
                {
                    ExecutableMissing();
                }
                else
                {
                    using (StreamWriter writer = new StreamWriter("THFDownloader.cfg", true))
                    {
                        writer.WriteLine("EQLocation=" + Application.StartupPath);
                    }
                    LoadConfiguration(); //Load .cfg to get the client version
                    if (!panelBrowse.Visible)
                    {
                        ExecutableFound(); // Modify UI
                        DownloadIndex(); // Download latest index
                    }
                }
            } else {
                LoadConfiguration(); //Load .cfg to get the client version
                if (!panelBrowse.Visible)
                {
                    ExecutableFound(); // Modify UI
                    DownloadIndex(); // Download latest index
                }
            }
        }

        private void ExecutableMissing()
        {
            statusDownloader.Visible = false;
            btnLaunch.Enabled = false;
            btnLaunch.Text = "PLAY!";
            panelBrowse.Visible = true;
        }

        private void ExecutableFound()
        {
            statusDownloader.Visible = true;
            btnLaunch.Enabled = false;
            btnLaunch.Text = "Installing ...";
            panelBrowse.Visible = false;
        }

        // INITIAL SETUP OF CONFIG FILE
        private void LoadConfiguration()
        {
            var lines = File.ReadLines("THFDownloader.cfg");

            foreach (var line in lines)
            {
                string[] lineName = line.Split(new Char[] { '=' });
                toolLocation.Text = Convert.ToString(lineName[1]);
            }

            if (File.Exists(toolLocation.Text + @"\" + "eqgame.exe"))
            {
                string[] dateArray = { "10/30/2005", "10/31/2005", "11/01/2005", "09/06/2007", "09/07/2007", "09/08/2007", "12/18/2008", "12/19/2008", "12/20/2008", "06/07/2010", "06/08/2010", "06/09/2010", "02/04/2011", "02/05/2011", "02/06/2011" };

                //int pos = Array.IndexOf(dateArray, Convert.ToString(RetrieveLinkerTimestamp(Path.Combine(Path.GetFullPath(toolLocation.Text).Replace(@"\", @"\\") + @"\" + "eqgame.exe")).ToString("MM/dd/yyyy").Replace(@"-", @"/")));

                int pos = Array.IndexOf(dateArray, DataVerify(Convert.ToString(RetrieveLinkerTimestamp(Path.Combine(Path.GetFullPath(toolLocation.Text).Replace(@"\", @"\\") + @"\" + "eqgame.exe")).ToString("MM/dd/yyyy"))));

                if (pos >= 0 && pos <= 2) // Titanium Version - Oct 31, 2005 --- 0,1,2
                {
                    lblEQType.Text = "DETECTED: Titanium";
                    toolType.Text = "Titanium";
                    toolClientVersion.Text = "Titanium";
                    toolType.Tag = "1";
                }
                else if (pos >= 3 && pos <= 5) // SoF Version - Sep 07, 2007 --- 3,4,5
                {
                    lblEQType.Text = "DETECTED: Secrets of Faydwer";
                    toolType.Text = "Secrets of Faydwer";
                    toolClientVersion.Text = "SoD";
                    toolType.Tag = 2;
                }
                else if (pos >= 6 && pos <= 8) // SoD Version - Dec 19, 2008 --- 6,7,8
                {
                    lblEQType.Text = "DETECTED: Seeds of Destruction";
                    toolType.Text = "Seeds of Destruction";
                    toolClientVersion.Text = "SoD";
                    toolType.Tag = 3;
                }
                else if (pos >= 9 && pos <= 11) // UF Version - June 8, 2010 --- 9,10,11
                {
                    lblEQType.Text = "DETECTED: Underfoot";
                    toolType.Text = "Underfoot";
                    toolClientVersion.Text = "SoD";
                    toolType.Tag = 4;
                }
                else if (pos >= 12 && pos <= 14) // Steam Version - Feb 05, 2011 --- 12,13,14
                {
                    lblEQType.Text = "DETECTED: UF Steam Edition";
                    toolType.Text = "Underfoot - Steam";
                    toolClientVersion.Text = "SoD";
                    toolType.Tag = 5;
                }
                else
                {
                    lblEQType.Text = "UNSUPPORTED: Please contact THF Admins.";
                    toolType.Text = "";
                    toolClientVersion.Text = "";
                    toolType.Tag = 0;

                    ExecutableMissing(); //Hide main UI and display search panel for executable.
                }
            }
            else
            {
                lblLocationError.Visible = true;
                ExecutableMissing();
            }
        }

        static string DataVerify(string date)
        {
            StringBuilder s  = new StringBuilder(date);
            s.Replace(@"-", @"/");
            s.Replace(@".", @"/");
            s.Replace(@",", @"/");
            s.Replace(@"_", @"/");
            return s.ToString();
        }

        private void DownloadIndex()
        {
            lblDownload.Text = "Initializing, Please Wait...";
            pbTotal.Tag = "INDEX";
            DownloadFiles();
        }


        private void CompareIndex()
        {
            //int addCounter = 0;
            //int valCounter = 0;

            var lines = File.ReadLines(toolClientVersion.Text + ".index");
            foreach (var line in lines)
            {
                string[] lineName = line.Split(new Char[] { '|' });

                if (!File.Exists(toolLocation.Text + "\\" + Convert.ToString(lineName[0]))) // IF FILE DOESN'T EXIST, ADD TO QUEUE.
                {
                    //addCounter++;
                    if (Convert.ToInt32(toolType.Tag) == 1) // Adding Titanium Files
                    {
                        _files.Enqueue(pbBackground.Tag + "TITANIUM/" + Convert.ToString(lineName[0]));
                    }
                    if (Convert.ToInt32(toolType.Tag) > 1) // Adding SoD+ Files
                    {
                        _files.Enqueue(pbBackground.Tag + "SOD/" + Convert.ToString(lineName[0]));
                    }
                }
                else // IF FILE EXISTS AND HASH DOES NOT MATCH, ADD TO QUEUE.
                {
                    //valCounter++;
                    string localHash = GetMD5HashFromFile(toolLocation.Text + "\\" + Convert.ToString(lineName[0]));
                    if (lineName[1] != localHash) 
                    {
                        if (Convert.ToInt32(toolType.Tag) == 1) // Adding Titanium Files
                        {
                            _files.Enqueue(pbBackground.Tag + "TITANIUM/" + Convert.ToString(lineName[0]));
                        }
                        if (Convert.ToInt32(toolType.Tag) > 1) // Adding SoD+ Files
                        {
                            _files.Enqueue(pbBackground.Tag + "SOD/" + Convert.ToString(lineName[0]));
                        }
                    }
                }
            }
            if (File.Exists("TITANIUM.index"))
            {
                File.Delete("TITANIUM.index");
            }
            if (File.Exists("SOD.index"))
            {
                File.Delete("SOD.index");
            }
            DownloadFiles();
            //Console.WriteLine("Added: " + addCounter);
            //Console.WriteLine("Validated: " + valCounter);
        }

// WEBCLIENT LOGIC
        public void DownloadFiles()
        {
            try
            {
                WebClient client = new WebClient();
                client.DownloadProgressChanged += client_DownloadProgressChanged;
                client.DownloadFileCompleted += client_DownloadFileCompleted;

                btnLaunch.Enabled = false;
                btnCheckForUpdate.Enabled = false;

                if (pbTotal.Tag.ToString() == "INDEX") // If the file is an index file...
                {
                    sw.Start();
                    if (Convert.ToInt32(toolType.Tag) == 1) // Adding Titanium Files
                    {
                        client.DownloadFileAsync(new Uri(pbBackground.Tag + "TITANIUM.index"), "TITANIUM.index");
                    }
                    if (Convert.ToInt32(toolType.Tag) > 1) // Adding SoD+ Files
                    {
                        client.DownloadFileAsync(new Uri(pbBackground.Tag + "SOD.index"), "SOD.index");
                    }
                }
                else
                {
                    // Create all folders if they don't exist
                    if (!Directory.Exists(toolLocation.Text + "\\" + "ActorEffects"))
                    {
                        Directory.CreateDirectory(toolLocation.Text + "\\" + "ActorEffects");
                    }
                    if (!Directory.Exists(toolLocation.Text + "\\" + "EnvEmitterEffects"))
                    {
                        Directory.CreateDirectory(toolLocation.Text + "\\" + "EnvEmitterEffects");
                    }
                    if (!Directory.Exists(toolLocation.Text + "\\" + "Resources"))
                    {
                        Directory.CreateDirectory(toolLocation.Text + "\\" + "Resources");
                    }
                    if (!Directory.Exists(toolLocation.Text + "\\" + "SpellEffects"))
                    {
                        Directory.CreateDirectory(toolLocation.Text + "\\" + "SpellEffects");
                    }
                    if (!Directory.Exists(toolLocation.Text + "\\" + "uifiles"))
                    {
                        Directory.CreateDirectory(toolLocation.Text + "\\" + "uifiles");
                    }
                    if (!Directory.Exists(toolLocation.Text + "\\" + "uifiles/default"))
                    {
                        Directory.CreateDirectory(toolLocation.Text + "\\" + "uifiles/default");
                    }

                    // Cycle through files that need to be downloaded.
                    if (_files.Any())
                    {
                        toolFilesToUpdate.Text = "Files to Update: ";
                        toolFilesRemaining.Text = Convert.ToString(_files.Count);
                        var url = _files.Dequeue();
                        sw.Start();

                        if (Convert.ToInt32(toolType.Tag) == 1) // Adding Titanium Files
                        {
                            string[] FileName = url.Split(new string[] { "TITANIUM/" }, StringSplitOptions.None);
                            lblDownload.Text = "Downloading: " + Convert.ToString(FileName[1]);
                            client.DownloadFileAsync(new Uri(url), toolLocation.Text + "\\" + Convert.ToString(FileName[1]));
                        }
                        if (Convert.ToInt32(toolType.Tag) > 1) // Adding SoD+ Files
                        {
                            string[] FileName = url.Split(new string[] { "SOD/" }, StringSplitOptions.None);
                            lblDownload.Text = "Downloading: " + Convert.ToString(FileName[1]);
                            client.DownloadFileAsync(new Uri(url), toolLocation.Text + "\\" + Convert.ToString(FileName[1]));
                        }

                        return;
                    }
                    else
                    {
                        // End of the download
                        btnLaunch.Enabled = true;
                        btnLaunch.Text = "PLAY!";
                        btnLaunch.BackgroundImage = THFDownloader.Properties.Resources.btnplay;
                        btnCheckForUpdate.Enabled = true;
                        toolFilesToUpdate.Text = "   ";
                        toolFilesRemaining.Text = "   ";
                        lblDownload.Text = "Up To Date!";
                    }
                }
            }
            catch
            {
                MessageBox.Show("Please check your internet connection.");
            }
        }

// DOWNLOAD IN PROGRESS UPDATES/LOGIC
        void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            try
            {
                //lblDownload.Text = "Downloading " + Convert.ToString(lblDownload.Tag);

                // Calculate Download Speed
                //if (lblPercentage.Text != (Convert.ToDouble(e.BytesReceived) / 1024 / sw.Elapsed.TotalSeconds).ToString("0"))
                    //lblSpeed.Text = (Convert.ToDouble(e.BytesReceived) / 1024 / sw.Elapsed.TotalSeconds).ToString("0.00") + " kb/s";

                // Update Progress Bar
                if (pbTotal.Value != e.ProgressPercentage) {
                    pbTotal.Value = e.ProgressPercentage;
                    pbTotal.Refresh();
                    
                    //int percent = (int)(((double)pbTotal.Value / (double)pbTotal.Maximum) * 100);
                    //pbTotal.CreateGraphics().DrawString(percent.ToString() +"%", new Font("Arial", (float)8.25, FontStyle.Regular), Brushes.Black, new PointF(pbTotal.Width - 35, pbTotal.Height / 2 - 7));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

// DOWNLOAD COMPLETE LOGIC
        private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            sw.Reset();

            if (e.Error != null)
            {
                // handle error scenario
                throw e.Error;
            }

            if (e.Cancelled == true)
            {
                lblDownload.Text = "Download Cancelled!";
                //lblSpeed.Text = "";
                //lblPercentage.Text = "";
            }
            else
            {
                //lblSpeed.Text = "";
                //lblPercentage.Text = "";

                if (pbTotal.Tag.ToString() == "INDEX")
                {
                    pbTotal.Tag = "";
                    CompareIndex();
                    return;
                }
                DownloadFiles(); // Continue downloading files in queue (if applicable)
            }
        }

// MD5 CRYPTO LOGIC - PASS IN FILE TO RETURN MD5
        protected string GetMD5HashFromFile(string fileName)
        {
            // Method for generating MD5 Hash
            FileStream file = new FileStream(fileName, FileMode.Open);
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(file);
            file.Close();

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("x2"));
            }
            return sb.ToString();
        }

// EQGAME CHECKER - COUNTS SECONDS FROM 1970 AND CREATES DATE BASED OFF OF ASSEMBLY.
        private DateTime RetrieveLinkerTimestamp(string filePath)
        {
            const int c_PeHeaderOffset = 60;
            const int c_LinkerTimestampOffset = 8;
            byte[] b = new byte[2048];
            System.IO.Stream s = null;

            try
            {
                s = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                s.Read(b, 0, 2048);
            }
            finally
            {
                if (s != null)
                {
                    s.Close();
                }
            }

            int i = System.BitConverter.ToInt32(b, c_PeHeaderOffset);
            int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);
            DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
            dt = dt.AddSeconds(secondsSince1970);
            dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);
            Console.WriteLine(dt.ToUniversalTime());
            return dt.ToUniversalTime();
        }

// CHECK FOR UPDATES - CLICKING CHECK
        private void btnCheckForUpdate_Click(object sender, EventArgs e)
        {
            if (!File.Exists("THFDownloader.cfg")) //Check for the configuration file, if not found then push the settings box
            {
                //If no .cfg, then force player to set location of eqgame.exe
                ExecutableMissing();
            }
            else
            {
                ExecutableFound(); // Validating Executable
                LoadConfiguration(); //Load .cfg to get the client version
                DownloadIndex(); // Download latest index
            }
        }

        private void btnLaunch_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start(toolLocation.Text + "\\" + "eqgame.exe", "patchme");
        }

        private void btnForums_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://www.thehiddenforest.org/portal.php");
        }

        private void txtLocation_TextChanged(object sender, EventArgs e)
        {
            if (txtLocation.Text == "" || !File.Exists(Path.Combine(Path.GetFullPath(txtLocation.Text).Replace(@"\", @"\\") + @"\" + "eqgame.exe")))
            {
                lblLocationError.Visible = true;
                return;
            }
            else
            {
                lblLocationError.Visible = false;

                if (File.Exists(txtLocation.Text + @"\" + "eqgame.exe"))
                {
                    string[] dateArray = { "10/30/2005", "10/31/2005", "11/01/2005", "09/06/2007", "09/07/2007", "09/08/2007", "12/18/2008", "12/19/2008", "12/20/2008", "06/07/2010", "06/08/2010", "06/09/2010", "02/04/2011", "02/05/2011", "02/06/2011" };

                    int pos = Array.IndexOf(dateArray, DataVerify(Convert.ToString(RetrieveLinkerTimestamp(Path.Combine(Path.GetFullPath(txtLocation.Text).Replace(@"\", @"\\") + @"\" + "eqgame.exe")).ToString("MM/dd/yyyy"))));

                    if (pos >= 0 && pos <= 2) // Titanium Version - Oct 31, 2005 --- 0,1,2
                    {
                        lblEQType.Text = "DETECTED: Titanium";
                        toolType.Text = "Titanium";
                        toolClientVersion.Text = "Titanium";
                        toolType.Tag = "1";
                    }
                    else if (pos >= 3 && pos <= 5) // SoF Version - Sep 07, 2007 --- 3,4,5
                    {
                        lblEQType.Text = "DETECTED: Secrets of Faydwer";
                        toolType.Text = "Secrets of Faydwer";
                        toolClientVersion.Text = "SoD";
                        toolType.Tag = 2;
                    }
                    else if (pos >= 6 && pos <= 8) // SoD Version - Dec 19, 2008 --- 6,7,8
                    {
                        lblEQType.Text = "DETECTED: Seeds of Destruction";
                        toolType.Text = "Seeds of Destruction";
                        toolClientVersion.Text = "SoD";
                        toolType.Tag = 3;
                    }
                    else if (pos >= 9 && pos <= 11) // UF Version - June 8, 2010 --- 9,10,11
                    {
                        lblEQType.Text = "DETECTED: Underfoot";
                        toolType.Text = "Underfoot";
                        toolClientVersion.Text = "SoD";
                        toolType.Tag = 4;
                    }
                    else if (pos >= 12 && pos <= 14) // Steam Version - Feb 05, 2011 --- 12,13,14
                    {
                        lblEQType.Text = "DETECTED: UF Steam Edition";
                        toolType.Text = "Underfoot - Steam";
                        toolClientVersion.Text = "SoD";
                        toolType.Tag = 5;
                    }
                    else
                    {
                        lblEQType.Text = "UNSUPPORTED: Please contact THF Admins.";
                        toolType.Text = "";
                        toolClientVersion.Text = "";
                        toolType.Tag = 0;

                        ExecutableMissing(); //Hide main UI and display search panel for executable.
                    }
                }
                else
                {
                    lblLocationError.Visible = true;
                    ExecutableMissing();
                }
            }
        }

        private void btnLocation_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                this.txtLocation.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        private void btnComplete_Click(object sender, EventArgs e)
        {
            if (File.Exists("THFDownloader.cfg"))
            { //Only thing the .cfg holds is the location
                var lines = File.ReadAllLines("THFDownloader.cfg");
                lines[0] = "EQLocation=" + txtLocation.Text;
                File.WriteAllLines("THFDownloader.cfg", lines);
                this.Close();
            }
            else
            {
                using (StreamWriter writer = new StreamWriter("THFDownloader.cfg", true))
                {
                    writer.WriteLine("EQLocation=" + txtLocation.Text);
                }
                ExecutableFound();
                LoadConfiguration(); //Load .cfg to get the client version
                DownloadIndex(); // Download latest index
            }
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}
